/* -------------------------------------------------------------------
 *    Application class for RubiksCube
 *
 *    $File: Cuby.cpp
 *    $Date: 2013/01/31 $
 *    $Revision: #1 $
 * -------------------------------------------------------------------*/
#include "Cuby.h"

const int Cuby::xMapping[24] = { 3, 0, 1, 2, 11, 8, 9, 10, 19, 16, 17, 18, 13, 14, 15, 12, 23, 20, 21, 22, 7, 4, 5, 6 };
const int Cuby::yMapping[24] = { 4, 5, 6, 7, 12, 13, 14, 15, 11, 8, 9, 10, 16, 17, 18, 19, 0, 1, 2, 3, 21, 22, 23, 20 };
const int Cuby::zMapping[24] = { 20, 21, 22, 23, 7, 4, 5, 6, 0, 1, 2, 3, 10, 11, 8, 9, 17, 18, 19, 16, 14, 15, 12, 13 };

Cuby::Cuby(void) : orientation(0) {
	sidecolors = new int[6];
	rotation = new int[3];
};

void Cuby::rotateX(Rotation rot) {
	switch (rot) {
	case CLOCKWISE: 
		orientation = xMapping[orientation]; 
		break;
	case COUNTERCLOCKWISE:  // shortcut ;-)
		orientation = xMapping[orientation]; 
		orientation = xMapping[orientation]; 
		orientation = xMapping[orientation]; 		
		break;
	}
};

void Cuby::rotateY(Rotation rot) {
	switch (rot) {
	case CLOCKWISE: 
		orientation = yMapping[orientation]; 
		break;
	case COUNTERCLOCKWISE:  // shortcut ;-)
		orientation = yMapping[orientation]; 
		orientation = yMapping[orientation]; 
		orientation = yMapping[orientation]; 		
		break;
	}
};

void Cuby::rotateZ(Rotation rot) {	
	switch (rot) {
	case CLOCKWISE: 
		orientation = zMapping[orientation]; 
		break;
	case COUNTERCLOCKWISE:  // shortcut ;-)
		orientation = zMapping[orientation]; 
		orientation = zMapping[orientation]; 
		orientation = zMapping[orientation]; 
		break;
	}
};

void Cuby::resetOrientation(void) {
	orientation = 0;
}

int Cuby::getOrientation(void) const {
	return orientation;
}

void Cuby::setOrientation(int* orient) {
	orientation = orient;
}

int* Cuby::getSidecolors(void) const {
	return sidecolors;
};

void Cuby::setSidecolors(int* colors) {
	sidecolors = colors;
};

/*!
 *  lookupGLRotation looks up the rotations needed in order to restore cubies orientation.
 *  The order of roation is X 1st, Y 2nd and Z last.
 */
int* Cuby::lookupGLRotation(void) {
	switch (orientation) {
		// white facing R
	case 0:	rotation[X_AXIS] = 0; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 0; break;
	case 1:	rotation[X_AXIS] = 90; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 0; break;
	case 2:	rotation[X_AXIS] = 180; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 0; break;
	case 3:	rotation[X_AXIS] = 270; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 0; break;
		// white facing F
	case 4:	rotation[X_AXIS] = 0; rotation[Y_AXIS] = 270; rotation[Z_AXIS] = 0; break;
	case 5:	rotation[X_AXIS] = 90; rotation[Y_AXIS] = 270; rotation[Z_AXIS] = 0; break;
	case 6:	rotation[X_AXIS] = 180; rotation[Y_AXIS] = 270; rotation[Z_AXIS] = 0; break;
	case 7:	rotation[X_AXIS] = 270; rotation[Y_AXIS] = 270; rotation[Z_AXIS] = 0; break;
		// white facing U
	case 8:	rotation[X_AXIS] = 0; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 90; break;
	case 9:	rotation[X_AXIS] = 90; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 90; break;
	case 10: rotation[X_AXIS] = 180; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 90; break;
	case 11: rotation[X_AXIS] = 270; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 90; break;
		// white facing L
	case 12: rotation[X_AXIS] = 0; rotation[Y_AXIS] = 180; rotation[Z_AXIS] = 0; break;
	case 13: rotation[X_AXIS] = 90; rotation[Y_AXIS] = 180; rotation[Z_AXIS] = 0; break;
	case 14: rotation[X_AXIS] = 180; rotation[Y_AXIS] = 180; rotation[Z_AXIS] = 0; break;
	case 15: rotation[X_AXIS] = 270; rotation[Y_AXIS] = 180; rotation[Z_AXIS] = 0; break;
		// white facing B
	case 16: rotation[X_AXIS] = 0; rotation[Y_AXIS] = 90; rotation[Z_AXIS] = 0; break;
	case 17: rotation[X_AXIS] = 90; rotation[Y_AXIS] = 90; rotation[Z_AXIS] = 0; break;
	case 18: rotation[X_AXIS] = 180; rotation[Y_AXIS] = 90; rotation[Z_AXIS] = 0; break;
	case 19: rotation[X_AXIS] = 270; rotation[Y_AXIS] = 90; rotation[Z_AXIS] = 0; break;
		// white facing D
	case 20: rotation[X_AXIS] = 0; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 270; break;
	case 21: rotation[X_AXIS] = 90; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 270; break;
	case 22: rotation[X_AXIS] = 180; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 270; break;
	case 23: rotation[X_AXIS] = 270; rotation[Y_AXIS] = 0; rotation[Z_AXIS] = 270; break;
	}
	return rotation;
}

void Cuby::draw(GLfloat size)
{
	GLfloat n[6][3] =
	{
		{1.0, 0.0, 0.0},
		{-1.0, 0.0, 0.0},
		{0.0, 1.0, 0.0},
		{0.0, -1.0, 0.0},
		{0.0, 0.0, 1.0},
		{0.0, 0.0, -1.0}
	};
	GLint faces[6][4] =	{ 
		{7, 6, 5, 4},  // RIGHT
		{0, 1, 2, 3},  // LEFT
		{3, 2, 6, 7},  // UPPER
		{4, 5, 1, 0},  // DOWN
		{5, 6, 2, 1},  // FRONT
		{7, 4, 0, 3}   // BACK
	};
	GLfloat v[8][3];
	GLint i;
	// x-coords
	v[0][X_AXIS] = v[1][X_AXIS] = v[2][X_AXIS] = v[3][X_AXIS] = -size / 2;
	v[4][X_AXIS] = v[5][X_AXIS] = v[6][X_AXIS] = v[7][X_AXIS] = size / 2;
	// y-coords
	v[0][Y_AXIS] = v[1][Y_AXIS] = v[4][Y_AXIS] = v[5][Y_AXIS] = -size / 2;
	v[2][Y_AXIS] = v[3][Y_AXIS] = v[6][Y_AXIS] = v[7][Y_AXIS] = size / 2;
	// z-coords
	v[0][Z_AXIS] = v[3][Z_AXIS] = v[4][Z_AXIS] = v[7][Z_AXIS] = -size / 2;
	v[1][Z_AXIS] = v[2][Z_AXIS] = v[5][Z_AXIS] = v[6][Z_AXIS] = size / 2;
	// decode glRotation from orentation
	int* rotations = lookupGLRotation();
	// reverse order because of stack behaviour
	glRotatef(rotation[Z_AXIS], 0.0f, 0.0f, 1.0f);
	glRotatef(rotation[Y_AXIS], 0.0f, 1.0f, 0.0f);
	glRotatef(rotation[X_AXIS], 1.0f, 0.0f, 0.0f);
	// colorize faces
	for (i = 0; i < 6; ++i) {
		glBegin(GL_QUADS);
			switch(sidecolors[i]) {
			case WHITE: 
				glColor3f(1.0f, 1.0f, 1.0f); break;
			case YELLOW: 
				glColor3f(1.0f, 1.0f, 0.0f); break;
			case RED: 
				glColor3f(1.0f, 0.0f, 0.0f); break;
			case ORANGE: 
				glColor3f(1.0f, 0.6f, 0.0f); break;
			case BLUE: 
				glColor3f(0.0f, 0.0f, 0.7f); break;
			case GREEN:
				glColor3f(0.0f, 0.8f, 0.0f); break;
			case BLACK:
			default: 
				glColor3f(0.0f, 0.0f, 0.0f); break;
			}
			glNormal3fv(&n[i][0]);
			glVertex3fv(&v[faces[i][0]][0]);
			glVertex3fv(&v[faces[i][1]][0]);
			glVertex3fv(&v[faces[i][2]][0]);
			glVertex3fv(&v[faces[i][3]][0]);
		glEnd();
	}
}